﻿using System.Linq;
using System.Diagnostics.Contracts;
using System;
using NHibernate;
using NHibernate.Linq;

namespace OpenWaves.ActivityFeed.NHibernate
{
    public class NHibernateActivityFeedService: IActivityFeedService
    {
        private readonly INHibernateSessionProvider _sessionProvider;

        public NHibernateActivityFeedService(): this(ServiceLocator.Resolve<INHibernateSessionProvider>())
        {            
        }

        public NHibernateActivityFeedService(INHibernateSessionProvider sessionProvider)
        {
            Contract.Requires<ArgumentNullException>(sessionProvider != null);
            this._sessionProvider = sessionProvider;
        }

        public NHibernateActivityFeedService(ISession session)
        {
            Contract.Requires<ArgumentNullException>(session != null);
            this._sessionProvider = new NHibernateSessionWrapper(session);
        }

        public void Subscribe(string subscriberId, System.Collections.Generic.IEnumerable<Topic> topics)
        {
            var subscriber = this.EnsureSubscriber(subscriberId);
            subscriber.SubscribeToTopics(topics);
            this._sessionProvider.Session.SaveOrUpdate(subscriber);
        }

        public void Unsubscribe(string subscriberId, System.Collections.Generic.IEnumerable<Topic> topics)
        {
            var subscriber = this._sessionProvider.Session.Get<Subscriber>(subscriberId);
            Contract.Assert(subscriber != null, "Cannot unsubscribe from topics. Subscriber does not exist.");

            subscriber.UnsubscribeFromTopics(topics);
            this._sessionProvider.Session.SaveOrUpdate(subscriber);
        }

        public Subscriber GetSubscriber(string subscriberId)
        {
            var subscriber = this._sessionProvider.Session.Get<Subscriber>(subscriberId);
            return subscriber ?? new Subscriber(subscriberId);
        }

        public IQueryable<Subscriber> GetSubscribers()
        {
            return this._sessionProvider.Session.Query<Subscriber>();
        }

        public Update GetUpdate(string updateId)
        {
            return this._sessionProvider.Session.Get<Update>(updateId);
        }

        public IQueryable<Update> GetUpdates()
        {
            return this._sessionProvider.Session.Query<Update>().OrderByDescending(u => u.PublishedOn);
        }

        public IQueryable<Update> SearchUpdates(string searchTerms = null)
        {
            throw new NotImplementedException();
        }

        public Update PublishUpdate(UpdateContent content, string publisherId, string source, System.Collections.Generic.IEnumerable<Topic> topics, bool isPublic, System.Collections.Generic.IEnumerable<UserGroup> userGroups)
        {
            var updateToPublish = new Update(content, publisherId, source, topics, isPublic, userGroups);
            this._sessionProvider.Session.Save(updateToPublish);
            return updateToPublish;
        }

        public void RemoveUpdate(Update update)
        {            
            this._sessionProvider.Session.Delete(update);
        }

        public void RemoveUpdatesOlderThan(DateTime dateTime)
        {
            var queryString = String.Concat("delete from ", typeof(Update).FullName, " where PublishedOn < :dateParam");            
            _sessionProvider.Session.CreateQuery(queryString).SetDateTime("dateParam", dateTime).ExecuteUpdate();
        }

        private Subscriber EnsureSubscriber(string subscriberId)
        {
            var subscriber = this._sessionProvider.Session.Get<Subscriber>(subscriberId);
            if (subscriber == null)
            {
                subscriber = new Subscriber(subscriberId);
                this._sessionProvider.Session.Save(subscriber);
            }
            return subscriber;
        }
    }
}